
import ab_ids as ai

from ab_object import AtrAttribution as Attr


class ObjectDisp:

    _LINE_LENGTH = 150
    _TREE_DISPLAY_LIST = {
        Attr.Standard: ai.CfgParaTreeDispFormat,
        Attr.Simplest: ai.CfgParaTreeSimplestDispFormat,
        Attr.Simple: ai.CfgParaTreeSimpleDispFormat
    }

    def __init__(self, app, objectp):
        self.app = app
        self.object = objectp

        self.usesimple = self.app.readKey(ai.CfgParaTreeUseSimpleAsDefault)

    def __str__(self):
        return self.getDisplay()

    def __safestr(self, strp, segment=None):
        ret = ''

        try:
            str_ = str('' if strp is None else strp)
        except:
            str_ = ''

        def _append(size):
            if segment:
                if len(segment) > 0:
                    segment.append(segment[-1] + size)
                else:
                    segment.append(size)

        for k in str_:
            v = ord(k)
            if 32 <= v < 127:
                ret += k
                _append(1)
            elif v == 0x0a:
                ret += '\\r'
                _append(2)
            elif v == 0x0d:
                ret += '\\n'
                _append(2)
            else:
                ret += '\\x%02x' % v
                _append(4)

        return ret

    def __shorten(self, str_, size, segment=None):
        if segment:
            for k, v in enumerate(segment):
                if k > 0 and v > size:
                    return str_[:segment[k - 1]] + '...'

        if len(str_) > size:
            return str_[:size] + '...'
        else:
            return str_

    def getString(self):
        # initialize the format string
        template = backup = self.app.readKey(
            ai.CfgParaTreeSimpleDispFormat
            if self.usesimple else ai.CfgParaTreeDispFormat)
        # check the attribute
        attribute = self.object.getAttribution()
        for key, format in self._TREE_DISPLAY_LIST.items():
            if attribute.get() & key == key:
                template = self.app.readKey(format)
                # if no format is defined, use the setting
                if template is None:
                    template = backup
                break

        offset, size = self.object.extract('ol')
        ret = template.replace('%s',
                               self.__safestr(self.object.extract('s'))) \
                      .replace('%o', str(offset)) \
                      .replace('%l', str(size)) \
                      .replace('%v', self.object.extract('v'))

        segment = list()
        content = self.__safestr(self.object.extract('c'), segment)
        if len(ret) + len(content) > self._LINE_LENGTH:
            content = self.__shorten(
                content, self._LINE_LENGTH - len(ret), segment)

        ret = ret.replace('%c', content)
        return ret

    @staticmethod
    def detectFileFormat(app, afile, bulk_size=10240,
                         notify=None, forcetext=False):
        spn = [0]
        off, clrf = 0, False

        if forcetext:
            binary = False
        else:
            binary = not app.readKey(ai.CfgParaEnableTextMode)

        afile.seek(0)
        while not binary and off < len(afile):
            if notify:
                notify(100 * off / len(afile), 'collect file data...')

            data = afile.read(bulk_size, off)
            if len(data) == 0:
                break
            for (k, c) in enumerate(data):
                od = ord(c)
                if od == 0x0d or od == 0x0a:
                    if clrf:
                        spn[-1] = off + k + 1
                    else:
                        spn.append(off + k + 1)

                    clrf = True
                elif c != '\t' and (od < 0x20 or od >= 0x7f):
                    binary = True
                    break
                else:
                    clrf = False

            off += len(data)

        if binary:
            spn = list()
        else:
            if spn[-1] != len(afile):
                spn.append(len(afile))

        return binary, spn
